home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1996 #15 / Monster Media Number 15 (Monster Media)(July 1996).ISO / prog_d / aspell22.zip / MEMOCHK.PAS < prev    next >
Pascal/Delphi Source File  |  1996-04-03  |  23KB  |  435 lines

  1. unit Memochk;
  2.  
  3. interface
  4.  
  5. { Revisions:
  6.     01/02/96 - Corrected SyncBuffer.  It was not getting the last
  7.                character in the TMemo's buffer.
  8.     01/07/96 - Improved handling of hyphenated words.
  9.     01/09/96 - Added Orpheus Editor component.
  10.     01/11/96 - Added Selection spell checking methods.
  11.     01/12/96 - Improved the look of the suggestion dialog box.
  12.     01/16/96 - Renamed TMemoSpellCheck to TMemoSpell.
  13. }
  14.  
  15. uses
  16.   SysUtils, WinTypes, WinProcs, Messages, Classes, {Graphics,} Controls,
  17.   Forms, Dialogs, StdCtrls, DBCtrls, SugDialg;
  18.  
  19. type SuggestionType = (stNoSuggest, stCloseMatch, stPhoneme);
  20.  
  21. type
  22.   TMemoSpell = class(TComponent)
  23.   private
  24.     { Private declarations }
  25.     FSuggestType         : SuggestionType;  { Holds the default initial suggestion type }
  26.     FDictionaryMain      : string;          { Holds the name of the main dictionary file }
  27.     FDictionaryUser      : string;          { Holds the name of the user's custom dictionary file }
  28.     FSuggestMax          : byte;            { Holds the maximum number of suggestions to return }
  29.     UserDictID           : integer;         { Holds the ID number ofhte open user dictionary }
  30.     FLeaveDictionaryOpen : boolean;         { Should we leave the dictionary files open? }
  31.     FDictionaryOpen      : boolean;         { Is the dictionary open? }
  32.     FAvoidHighlight      : boolean;         { Should the dialog avoid the highlighted text? }
  33.   protected
  34.     { Protected declarations }
  35.     DictDataPtr   : pointer;               { Pointer to internal dictionary data }
  36.     SuggestDlg    : TSugDialog;            { The dialog box for this component }
  37.     StartWord     : string;                { Temporary place to store the word being tested }
  38.     IgnoreList    : TStringList;           { List of words to ignore }
  39.     ReplaceList   : TStringList;           { Replacement word list }
  40.     AlternateList : TStringList;           { Replacement word alternate word list }
  41.     procedure BaseCheckMemo(var TheMemo : TMemo; CheckStart, CheckLength : integer);
  42.     procedure SetDialogPosition(var TheMemo : TMemo);
  43.   public
  44.     { Public declarations }
  45.     UserDictionaryOpen : boolean;                 { Record if the custom user dictionary was opened ok }
  46.     constructor Create(AOwner : TComponent); override;  { Standard create method }
  47.     procedure Free;                        { Standard free method }
  48.     procedure SetMaximumSuggestions(Max : byte);      { Method to set the maximum number of suggestions }
  49.     procedure SetDictionaryMain(Filename : string);   { Set a new main dictionary filename }
  50.     procedure SetDictionaryUser(Filename : string);   { Set a new user dictionary filename }
  51.     property DictionaryOpen : boolean read FDictionaryOpen;
  52.  published
  53.     { Published declarations }
  54.     procedure CheckMemo(TheMemo : TMemo);      { Main method, check the spelling of a TMemo }
  55.     procedure CheckMemoSelection(TheMemo : TMemo); { Alternate method, check the selected text only }
  56.     procedure CheckDBMemo(TheMemo : TDBMemo);  { Main method, check the spelling of a TDBMemo }
  57.     procedure CheckDBMemoSelection(TheMemo : TDBMemo);  { Alternate method, check the selected text only }
  58.     procedure ClearLists;                      { Method to clear the ignore/replace lists }
  59.     property SuggestType : SuggestionType read FSuggestType write FSuggestType default stCloseMatch;
  60.        { Get/Set the initial suggestion type }
  61.     property DictionaryMain : string read FDictionaryMain write SetDictionaryMain;
  62.        { Get/Set the name of the main dictionary file }
  63.     property DictionaryUser : string read FDictionaryUser write SetDictionaryUser;
  64.        { Get/Set the name of the user dictionary file }
  65.     property MaxSuggestions : byte read FSuggestMax write SetMaximumSuggestions default 10;
  66.        { Get/Set the maximum number of suggestions }
  67.     property LeaveDictionariesOpen : boolean read FLeaveDictionaryOpen write FLeaveDictionaryOpen default TRUE;
  68.        { Get/Set whether the dictionary should be opened/closed after each call }
  69.     property AvoidHighlight : boolean read FAvoidHighlight write FAvoidHighlight default true;
  70.        { Get/Set whether the highlight should be avoided by the dialog }
  71.   end;
  72.  
  73.  
  74. procedure Register;
  75.  
  76. implementation
  77.  
  78. uses BaseASpl;
  79.  
  80.  
  81. procedure Register;  { Standard component registration procedure }
  82. begin
  83.   RegisterComponents('Samples', [TMemoSpell]);
  84. end;
  85.  
  86.  
  87. constructor TMemoSpell.Create(AOwner : TComponent);
  88. { Standard create method }
  89. begin
  90.   inherited Create(AOwner);           { Make sure the base component to made }
  91.   FSuggestType := stCloseMatch;       { Set the default values }
  92.   FDictionaryMain := 'acrop.dct';
  93.   FDictionaryUser := 'custom.dct';
  94.   FLeaveDictionaryOpen := TRUE;
  95.   FDictionaryOpen  := FALSE;
  96.   UserDictionaryOpen := FALSE;
  97.   FSuggestMax     := 10;
  98.   FAvoidHighlight := true;
  99.   IgnoreList := TStringList.Create;    { Create the list of ignored words }
  100.   IgnoreList.Clear;                    { And set it to the way it is needed to be }
  101.   IgnoreList.Sorted := TRUE;
  102.   ReplaceList := TStringList.Create;   { Create the list of words to replace }
  103.   ReplaceList.Clear;                   { And set it up }
  104.   ReplaceList.Sorted := FALSE;
  105.   AlternateList := TStringList.Create; { Create the list of words to replace with }
  106.   AlternateList.Clear;                 { And set it up }
  107.   AlternateList.Sorted := FALSE;
  108.   InitDictionaryData(DictDataPtr);        { Create the internal dictionary data }
  109.   SuggestDlg := TSugDialog.Create(Self);  { Create the dialog box }
  110.   SuggestDlg.DictDataPtr := DictDataPtr;  { And let it know the internal data address }
  111. end;
  112.  
  113. procedure TMemoSpell.Free;
  114. { Standard free method }
  115. begin
  116.   if FDictionaryOpen then
  117.     BaseASpl.CloseDictionaries(DictDataPtr);
  118.   ReleaseDictionaryData(DictDataPtr);
  119.   IgnoreList.Free;     { Get rid of the ignore list }
  120.   ReplaceList.Free;    { Get rid of the replacement list }
  121.   AlternateList.Free;  { Get rid of the replacement word list }
  122.   SuggestDlg.Free;     { Get rid of the suggestion dialog box }
  123.   inherited Free;      { and then the base component }
  124. end;
  125.  
  126. procedure TMemoSpell.SetMaximumSuggestions(Max : byte);
  127. { Set the maximum number of suggestions to return }
  128. begin
  129.   FSuggestMax := Max;   { And store the value }
  130. end;
  131.  
  132. procedure TMemoSpell.SetDictionaryMain(Filename : string);
  133. begin
  134.   if FDictionaryOpen or UserDictionaryOpen then
  135.     begin
  136.       BaseASpl.CloseDictionaries(DictDataPtr);  { Close the dictionaries since filename is changing }
  137.       FDictionaryOpen := FALSE;                 { Mark them as not opened }
  138.       UserDictionaryOpen := FALSE;
  139.     end;
  140.   FDictionaryMain := Filename;
  141. end;
  142.  
  143. procedure TMemoSpell.SetDictionaryUser(Filename : string);
  144. begin
  145.   if FDictionaryOpen or UserDictionaryOpen then
  146.     begin
  147.       BaseASpl.CloseDictionaries(DictDataPtr);  { Close the dictionaries since filename is changing }
  148.       FDictionaryOpen := FALSE;                 { Mark them as not opened }
  149.       UserDictionaryOpen := FALSE;
  150.     end;
  151.   FDictionaryUser := Filename;
  152. end;
  153.  
  154. procedure TMemoSpell.ClearLists;
  155. begin
  156.   IgnoreList.Clear;                    { Clear the ignore list }
  157.   IgnoreList.Sorted := TRUE;
  158.   ReplaceList.Clear;                   { Clear the list of words to replace }
  159.   ReplaceList.Sorted := FALSE;
  160.   AlternateList.Clear;                 { Clear the list of words to do the replacing with }
  161.   AlternateList.Sorted := FALSE;
  162. end;
  163.  
  164. procedure TMemoSpell.SetDialogPosition(var TheMemo : TMemo);
  165. { Set the position of the Suggestion Dialog based on the current line.
  166.   If the dialog window and the editor area do not overlap, or there is no
  167.   possibility of the highlight being covered don't move it. }
  168. var EditorScreen : TPoint;
  169.     SelectBottom : integer;
  170.     RowHeight    : integer;
  171.     SelectLine   : integer;
  172.     TopLine      : integer;
  173. begin
  174.   EditorScreen := TheMemo.ClientToScreen(TheMemo.ClientRect.TopLeft);
  175.   if ((EditorScreen.X+TheMemo.Width) < SuggestDlg.Left) or
  176.      (EditorScreen.X > (SuggestDlg.Left+SuggestDlg.Width)) or
  177.      ((EditorScreen.Y+TheMemo.Height) < SuggestDlg.Top) or
  178.      (EditorScreen.Y > (SuggestDlg.Top+SuggestDlg.Height)) then
  179.     exit;  { Not in editor area so exit without bothering to move the dialog }
  180.   { Figure out where the current line really is on the screen }
  181.   SelectLine := SendMessage(TheMemo.Handle, EM_LINEFROMCHAR, $FFFF, 0);
  182.   TopLine    := SendMessage(TheMemo.Handle, EM_GETFIRSTVISIBLELINE, 0, 0);
  183.   { Calculate the height of the rows of text }
  184.   RowHeight := ABS(ROUND(TheMemo.Font.Height * (TheMemo.Font.PixelsPerInch / 72)));
  185.   SelectBottom := ((SelectLine-TopLine)+1)*RowHeight+EditorScreen.Y;
  186.   { See if the highlight could actually be covered by the dialog }
  187.   if (SelectBottom  < SuggestDlg.Top) or
  188.      ((SelectBottom-RowHeight) > (SuggestDlg.Top+SuggestDlg.Height)) then
  189.     exit;  { Not near the highlight, exit without moving }
  190.   { It could be covering the highlight, so move to the top or bottom of screen }
  191.   if SelectBottom > (Screen.Height div 2) then
  192.     SuggestDlg.Top := 20
  193.   else
  194.     SuggestDlg.Top := Screen.Height-SuggestDlg.Height-20;
  195. end;
  196.  
  197. procedure TMemoSpell.BaseCheckMemo(var TheMemo : TMemo; CheckStart, CheckLength : integer);
  198. { The main method for this component.  Test the spelling of the text in the passed memo }
  199. type LargeBuffer = array[0..32800] of char; { A little over 32K - the limit on memo's size }
  200.      LargeBufferPtr = ^LargeBuffer;
  201. var Done       : boolean;        { Loop control }
  202.     OldHide    : boolean;        { Storage for the original state of the HideSelection property }
  203.     Changed    : boolean;        { Was anything in the memo changed? }
  204.     EmptyList  : TStringList;    { Empty list in case user dictionary need to be made }
  205.     HoldBuffer : LargeBufferPtr; { Buffer to speed up finding words }
  206.     Start      : integer;        { Start of the word }
  207.     WordEnd    : integer;        { End of the word }
  208.     CheckLoc   : integer;        { Location we are currently checking }
  209.     TheResult  : integer;        { Temporary ShowModal return storage }
  210.   procedure SyncBuffer;
  211.   { Duplicate the memo's text into the temporary buffer }
  212.   begin
  213.     TheMemo.GetTextBuf(HoldBuffer^, TheMemo.GetTextLen+1);
  214.     { No need to worry about the length.  TMemo buffers are 32K or smaller }
  215.   end;
  216.   function GetNextWord : string;
  217.   { Get the next word in the memo }
  218.   var CurrentTextLen    : integer;  { Temporary to hold length of memo's text }
  219.       CurrentPos        : integer;
  220.       S                 : string;
  221.   begin
  222.     { Scan until we find the start of a word.  Defined as someting starting with a letter }
  223.     CurrentTextLen := TheMemo.GetTextLen;  { Just to speed things up a litte }
  224.     CurrentPos := CheckLoc;         { Start at the selection }
  225.     while (CurrentPos < CurrentTextLen) and
  226.            (not (HoldBuffer^[CurrentPos] in ['A'..'Z','a'..'z',        { The english letters and }
  227.                                              #138,#140,#159,           { non-english characters  }
  228.                                              #192..#214,#216..#223,#240,
  229.                                              #154,#156,#224..#239,
  230.                                              #241..#246,#248..#255])) do
  231.       Inc(CurrentPos);  { Move to the next character }
  232.     Start := CurrentPos;   { Record the actual start of the word }
  233.     { Find the end of the word.  The word ends when a non-letter character }
  234.     { or the character "'" is found.  }
  235.     S := '';
  236.     while (CurrentPos < CurrentTextLen) and
  237.             (HoldBuffer^[CurrentPos] in ['A'..'Z','a'..'z','''',
  238.                                          #138,#140,#159,
  239.                                          #192..#214,#216..#223,#240,
  240.                                          #154,#156,#224..#239,
  241.                                          #241..#246,#248..#255]       ) do
  242.       begin
  243.         S := S + HoldBuffer^[CurrentPos];   { Add it to the current word }
  244.         Inc(CurrentPos);  { Move to the next character }
  245.       end;
  246.     WordEnd := CurrentPos;                   { Save the end of the word }
  247.     GetNextWord := S;                        { Return the found word }
  248.   end;
  249. begin
  250.   try
  251.   HoldBuffer := NIL;
  252.   New(HoldBuffer);    { Create a temporary buffer to hold a copy of the memo's text }
  253.   Changed := FALSE;  { Nothing has been changed yet. }
  254.   OldHide := TheMemo.HideSelection;         { Save the old HideSelection property }
  255.   TheMemo.HideSelection := FALSE;           { and make sure selections are shown }
  256.   SuggestDlg.MaxSuggest := FSuggestMax;  { Set the maximum number of suggestions }
  257.   if not FDictionaryOpen then  { Check to see if the dictionary is already open }
  258.     begin
  259.       FDictionaryOpen := BaseASpl.OpenDictionary(DictDataPtr, FDictionaryMain);  { Open the dictionaries }
  260.       if not FDictionaryOpen then
  261.         begin
  262.           MessageDlg('Could not open dictionary', mtError, [mbOK], -1);
  263.           exit;
  264.         end;
  265.       UserDictID := BaseASpl.OpenUserDictionary(DictDataPtr, FDictionaryUser);  { And record if they actually opened }
  266.       if UserDictID < 0 then        { Didn't open so try to make one }
  267.         begin
  268.           EmptyList := TStringList.Create;   { Create and clear to make an empty list }
  269.           EmptyList.Clear;
  270.           UserDictID := BaseASpl.BuildUserDictionary(DictDataPtr, FDictionaryUser, EmptyList);  { Build dictionary }
  271.           EmptyList.Free;  { Free the empty list }
  272.         end;
  273.       UserDictionaryOpen := UserDictID >= 0;  { Check to see if dictionary was opened/made }
  274.     end;
  275.   SyncBuffer;  { Load the text into a easy to access buffer }
  276.   with SuggestDlg do  { The suggestion dialog is used a lot so make it easily accessible }
  277.     begin
  278.       TheMemo.SelLength := 0;   { Set up no selection and move to the }
  279.       TheMemo.SelStart := 0;    { start of the section to check }
  280.       CheckLoc := CheckStart;   { Start at the section to spell check }
  281.       SuggestDlg.Caption := 'Suggestions: Scanning...';  { Tell the user we're scanning the text }
  282.       if FAvoidHighlight then  { Calculate a window position if avoiding the highlight }
  283.         begin
  284.           SuggestDlg.Top := (Screen.Height div 2) - (SuggestDlg.Height div 2);  { Position dialog in center of screen }
  285.           SuggestDlg.Left := (Screen.Width div 2) - (SuggestDlg.Width div 2);
  286.           SetDialogPosition(TheMemo);
  287.         end;
  288.       SuggestDlg.WordEdit.Text := '';   { Clear the fields in the dialog window }
  289.       SuggestDlg.SuggestList.Clear;
  290.       SuggestDlg.TheResult := 0;
  291.       SuggestDlg.DisableButtons;        { Disable all but the Cancel button }
  292.       Application.ProcessMessages;      { Give Windows time to draw the window }
  293.       Done := FALSE;            { Assume we aren't done }
  294.       repeat
  295.         StartWord := GetNextWord;       { Get the next word in the memo }
  296.         Application.ProcessMessages;    { Give Windows time to process mouse events }
  297.         IF not BaseASpl.GoodWord(DictDataPtr, StartWord) THEN  { Is the word in the dictionaries? }
  298.           if IgnoreList.IndexOf(Uppercase(StartWord)) = -1 then  { No, is it in the ignore list? }
  299.             begin  { Word not found and not ignored }
  300.               TheMemo.SelStart  := Start;             { Highlight the word }
  301.               TheMemo.SelLength := WordEnd - Start;
  302.               if ReplaceList.IndexOf(StartWord) = -1 then  { In the replacement list? }
  303.                 begin
  304.                   case FSuggestType of           { Build an inital list of suggestions }
  305.                     stCloseMatch : SuggestList.Items := BaseASpl.SuggestCloseMatch(DictDataPtr, StartWord, FSuggestMax);
  306.                     stPhoneme    : SuggestList.Items := BaseASpl.SuggestPhoneme(DictDataPtr, StartWord, FSuggestMax);
  307.                     stNoSuggest  : SuggestList.Clear;
  308.                   end;
  309.                   SuggestDlg.TheResult := 0;              { Clear the Dialog result }
  310.                   SuggestDlg.Caption := 'Suggestions';    { Remove "Scanning" from caption }
  311.                   if FAvoidHighlight then                 { Check if the highlight has to be avoided }
  312.                     SetDialogPosition(TheMemo);
  313.                   if not SuggestDlg.Visible then          { If dialog isn't visible, make it so }
  314.                     SuggestDlg.Show;
  315.                   SuggestDlg.EnableButtons;      { Enable all the dialog controls }
  316.                   WordEdit.Text := StartWord;    { Setup the Suggestion dialog }
  317.                   NotWord.Text := StartWord;     { Setup the Word we are checking }
  318.                   SuggestDlg.ActiveControl := BtnIgnore;    { Make the Ignore Button active control }
  319.                   Application.ProcessMessages;   { Allow Windows to update things }
  320.  
  321.                   repeat                            { Give Windows all the time until }
  322.                     Application.ProcessMessages;   { one of the buttons are pressed }
  323.                   until SuggestDlg.TheResult <> 0;
  324.                   SuggestDlg.DisableButtons;    { Disable the buttons }
  325.                   TheResult := SuggestDlg.TheResult;  { Find out what the user did }
  326.                end
  327.               else
  328.                 begin
  329.                   TheResult := 101;  { Fake Replace Button being pressed }
  330.                   WordEdit.Text := AlternateList.Strings[ReplaceList.IndexOf(StartWord)]; { And get the replacement word }
  331.                 end;
  332.                case TheResult of   { Display the suggestion dialog }
  333.                 100 : Done := TRUE;                            { Cancel - end the spell checking }
  334.                 101,
  335.                 105 : begin   { Replace }
  336.                         TheMemo.SelText := WordEdit.Text;        { Replace - replace the word with the correction }
  337.                         Changed := TRUE;
  338.                         SyncBuffer;                              { Resync the temp buffer }
  339.                         WordEnd := TheMemo.SelStart + TheMemo.SelLength;   { Reset the end of word }
  340.                         CheckLength := CheckLength + (Length(WordEdit.Text) - Length(StartWord)); { Adjust ending length }
  341.                         if TheResult = 105 then { Replace all occurences }
  342.                           begin
  343.                             ReplaceList.Add(StartWord);
  344.                             AlternateList.Add(WordEdit.Text);
  345.                           end;
  346.                       end;
  347.                       { Add - the questioned word to the user dictionary }
  348.                 102 : BaseASpl.AddWord(DictDataPtr, StartWord, UserDictID);
  349.                 103 : ; { Ignore just this occurence - Don't do anything }
  350.                 104 : IgnoreList.Add(Uppercase(StartWord));    { Ignore All - add the questioned word to the ignore list }
  351.               end;
  352.             end;
  353.         CheckLoc := WordEnd+1;  { Move to one character after the end of the current word }
  354.       until Done or (CheckLoc >= (CheckLength+CheckStart)) or (SuggestDlg.TheResult = 100);
  355.          { Canceled or end of the memo is reached }
  356.     end;
  357.   if SuggestDlg.Visible then   { Get rid of the dialog, if needed }
  358.     SuggestDlg.Hide;
  359.   if not Changed then    { Let the user know something actually happened }
  360.     MessageDlg('No changes made', mtInformation, [mbOK], -1)
  361.   else
  362.     MessageDlg('Checking complete', mtInformation, [mbOK], -1);
  363.   finally
  364.     Dispose(HoldBuffer);              { Release the temporary buffer }
  365.     if SuggestDlg.Visible then   { Get rid of the dialog, if needed }
  366.       SuggestDlg.Hide;
  367.     if not FLeaveDictionaryOpen then  { Check if the dictionaries should be closed }
  368.       begin
  369.         BaseASpl.CloseDictionaries(DictDataPtr);       { Close the dictionaries  }
  370.         FDictionaryOpen := FALSE;          { Mark them as not opened }
  371.         UserDictionaryOpen := FALSE;
  372.       end;
  373.     TheMemo.HideSelection := OldHide; { Restore the HideSelection property of the memo }
  374.   end;
  375. end;
  376.  
  377.  
  378. procedure TMemoSpell.CheckMemo(TheMemo : TMemo);
  379. begin
  380.   BaseCheckMemo(TheMemo, 0, TheMemo.GetTextLen+1);  { Check the whole memo }
  381. end;
  382.  
  383. procedure TMemoSpell.CheckMemoSelection(TheMemo : TMemo);
  384. var CheckStart, CheckLength : integer;
  385. begin
  386.   with TheMemo do
  387.     begin
  388.       if SelLength = 0 then  { Make sure there is something selected }
  389.         exit;                { If not then there is nothing to check }
  390.      { Make sure we have a whole word at the start of the selection }
  391.       CheckStart  := SelStart;   { Get the start of the selection }
  392.       CheckLength := SelLength;  { And the length }
  393.       SelLength := 1;  { Only look at one character at a time }
  394.       while (CheckStart <> 0) and (TheMemo.SelText[1] in ['A'..'Z','a'..'z',
  395.                                                           #138,#140,#159,
  396.                                                           #192..#214,#216..#223,#240,
  397.                                                           #154,#156,#224..#239,
  398.                                                           #241..#246,#248..#255]) do
  399.         begin
  400.           Dec(CheckStart);         { Move back another charater }
  401.           Inc(CheckLength);        { and expand the length to check }
  402.           if SelStart <> 0 then
  403.             SelStart := SelStart - 1;   { then look at the charcter before that }
  404.           SelLength := 1;
  405.         end;
  406.      { Now make sure we have a whole word at the end of the selection }
  407.       SelStart := CheckStart + CheckLength;  { Move to the end of the selected text }
  408.       SelLength := 1;  { Look at only a single charater }
  409.       while (SelStart < GetTextLen) and (SelText[1] in ['a'..'z','A'..'Z',
  410.                                                         #138,#140,#159,
  411.                                                         #192..#214,#216..#223,#240,
  412.                                                         #154,#156,#224..#239,
  413.                                                         #241..#246,#248..#255]) do
  414.         begin
  415.           Inc(CheckLength);          { Expand the selection length by one character }
  416.           if SelStart < GetTextLen then  { And move to the next if possible }
  417.             SelStart := SelStart + 1;
  418.           SelLength := 1;
  419.         end;
  420.     end;
  421.   BaseCheckMemo(TheMemo, CheckStart, CheckLength);  { Check the selected region }
  422. end;
  423.  
  424. procedure TMemoSpell.CheckDBMemo(TheMemo : TDBMemo);
  425. begin
  426.   CheckMemo(TMemo(TheMemo));
  427. end;
  428.  
  429. procedure TMemoSpell.CheckDBMemoSelection(TheMemo : TDBMemo);
  430. begin
  431.   CheckMemoSelection(TMemo(TheMemo));
  432. end;
  433.  
  434. end.
  435.